#pragma once
#include "CBC.h"
#include "lang/System.h"
#include "crypto/CryptoUtil.h"
#include "crypto/algorithms/ialgorithm/FAQBlockAlgorithm.h"

namespace yzrilyzr_crypto{
	ECLASS(CTS, public CBC){
	private:
	yzrilyzr_array::ByteArray prevCipher;
	public:
	CTS(yzrilyzr_array::ByteArray iv) :CBC(iv, 0, iv.length){

	}
	yzrilyzr_array::ByteArray encrypt(const yzrilyzr_array::ByteArray & block, FAQCipherAlgorithm & algorithm)override{
		return prevCipher=CBC::encrypt(block, algorithm);
	}
	yzrilyzr_array::ByteArray decrypt(const yzrilyzr_array::ByteArray & block, FAQCipherAlgorithm & algorithm)override{
		return prevCipher=CBC::decrypt(block, algorithm);
	}
	yzrilyzr_array::ByteArray encryptFinal(const yzrilyzr_array::ByteArray & block, FAQCipherAlgorithm & algorithm)override{
		u_index inputLength=block.length;
		u_index blockSize=((FAQBlockAlgorithm &)algorithm).getBlockSize();
		//if(blockLength<blockSize) throw new RuntimeException("update is too short!");
		// 完整块直接加密
		if(inputLength == blockSize) return encrypt(block, algorithm);
		// CTS处理流程
		// 1. 用 prevCipher 填充最后一个块
		yzrilyzr_array::ByteArray paddedBlock(16);
		yzrilyzr_lang::System::arraycopy(block, 0, paddedBlock, 0, block.length); // Pn
		yzrilyzr_lang::System::arraycopy(prevCipher, block.length, paddedBlock, block.length, 16 - block.length); // Cn-1 尾部
		// 2. 加密填充后的块
		CryptoUtil:: xor (paddedBlock, prevCipher, paddedBlock);
		yzrilyzr_array::ByteArray Cn_prime=algorithm.encrypt(paddedBlock);
		// 3. 构造最终密文
		yzrilyzr_array::ByteArray finalCiphertext(16 + block.length);
		yzrilyzr_lang::System::arraycopy(Cn_prime, 0, finalCiphertext, 0, 16);          // Cn_prime
		yzrilyzr_lang::System::arraycopy(prevCipher, 0, finalCiphertext, 16, block.length); // Cn-1[0:k]
		// 4. 更新 prevCipher（供后续加密）
		prevCipher=Cn_prime;
		return finalCiphertext;
	}
	yzrilyzr_array::ByteArray decryptFinal(const yzrilyzr_array::ByteArray & block, FAQCipherAlgorithm & algorithm)override{
		u_index blockLength=block.length;
		u_index blockSize=((FAQBlockAlgorithm &)algorithm).getBlockSize();
		if(blockLength < blockSize) throw  yzrilyzr_lang::RuntimeException("update is too short!");
		// 处理完整块情况
		if(blockLength == blockSize) return decrypt(block, algorithm);
		// CTS处理流程
		u_index nLeft=blockLength % blockSize;
		if(nLeft == 0){
			// 交换最后两个块
			u_index lastBlkIndex=blockLength - blockSize;
			u_index nextToLastBlkIndex=blockLength - 2 * blockSize;
			yzrilyzr_array::ByteArray tmp(2 * blockSize);
			yzrilyzr_lang::System::arraycopy(block, lastBlkIndex, tmp, 0, blockSize);
			yzrilyzr_lang::System::arraycopy(block, nextToLastBlkIndex, tmp, blockSize, blockSize);
			yzrilyzr_array::ByteArray decrypted=decrypt(tmp, algorithm);
			yzrilyzr_array::ByteArray result(blockLength);
			yzrilyzr_lang::System::arraycopy(block, 0, result, 0, nextToLastBlkIndex);
			yzrilyzr_lang::System::arraycopy(decrypted, 0, result, nextToLastBlkIndex, blockSize);
			yzrilyzr_lang::System::arraycopy(decrypted, blockSize, result, lastBlkIndex, blockSize);
			return result;
		} else{
			int newBlockLen=blockLength - (blockSize + nLeft);
			yzrilyzr_array::ByteArray newBlock(newBlockLen);
			yzrilyzr_lang::System::arraycopy(block, 0, newBlock, 0, newBlockLen);
			// 解密前面的完整块
			yzrilyzr_array::ByteArray decrypted=decrypt(newBlock, algorithm);
			// 处理最后两个块
			yzrilyzr_array::ByteArray lastTwoBlocks(2 * blockSize);
			yzrilyzr_lang::System::arraycopy(block, newBlockLen, lastTwoBlocks, 0, blockSize);
			yzrilyzr_lang::System::arraycopy(block, newBlockLen + blockSize, lastTwoBlocks, blockSize, nLeft);
			// 解密倒数第二个块
			yzrilyzr_array::ByteArray tmpDecrypted=algorithm.decrypt(lastTwoBlocks);
			// 构建结果
			yzrilyzr_array::ByteArray result(blockLength);
			if(newBlockLen > 0){
				yzrilyzr_lang::System::arraycopy(decrypted, 0, result, 0, newBlockLen);
			}
			// 处理最后部分
			for(int i=0;i < nLeft;i++){
				result[newBlockLen + blockSize + i]=(int8_t)(lastTwoBlocks[blockSize + i] ^ tmpDecrypted[i]);
			}
			// 解密最后一个块
			yzrilyzr_array::ByteArray lastDecrypted=algorithm.decrypt(tmpDecrypted);
			yzrilyzr_lang::System::arraycopy(lastDecrypted, 0, result, newBlockLen, blockSize);
			// 更新prevCipher
			prevCipher=lastDecrypted;
			return result;
		}
	}
	};
}